#include "list.h"

#include "mem.h"

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>

static void
test_create()
{
    List_T list;
    list = List_list("hello", "how", "are", "you", NULL);
    assert(List_length(list) == 4);
    assert(strcmp(list->first, "hello") == 0);
    List_free(&list);
    assert(list == NULL);

    list = List_list((void*)1, (void*)2, (void*)3, (void*)4, NULL);
    assert(List_length(list) == 4);
    assert(list->first == (void*)1);
    List_free(&list);
    assert(list == NULL);
}

static void
test_append()
{
    assert(List_append(NULL, NULL) == NULL);

    List_T list1 = List_list("hello", "how", NULL);

    List_T list = List_append(NULL, list1);
    assert(list == list1);
    assert(List_length(list) == 2);

    list = List_append(list1, NULL);
    assert(list == list1);
    assert(List_length(list) == 2);

    // List_list(list1, list1) is wrong
    List_T list2 = List_list("hello", "how", NULL);
    list = List_append(list2,
                       List_list((void*)100, (void*)200, NULL)); // make sure list->rest is NULL
    assert(list == list2);
    assert(List_length(list) == 4);
    assert(strcmp(list->first, "hello") == 0);
    List_free(&list);

    List_T list3 = List_list((void*)100, (void*)200, NULL);
    list = List_append(list3,
                       List_list("hello", "how", NULL)); // make sure list->rest is NULL
    assert(list == list3);
    assert(List_length(list) == 4);
    assert(list->first == (void*)100);
    List_free(&list);

    List_free(&list1);
}

static void
test_copy()
{
    assert(List_copy(NULL) == NULL);

    List_T list1 = List_list("hello", "how", NULL);

    List_T list = List_copy(list1);
    assert(list != list1);
    assert(List_length(list) == 2);
    assert(strcmp(list->first, "hello") == 0);
    List_free(&list);

    List_free(&list1);
}

static void
test_push_pop()
{
    assert(List_pop(NULL, NULL) == NULL);

    List_T list = List_push(NULL, "hello");
    list = List_push(list, "how");
    assert(List_length(list) == 2);

    char *s;
    list = List_pop(list, (void**)&s);
    assert(strcmp(s, "how") == 0);
    assert(List_length(list) == 1);

    list = List_pop(list, (void**)&s);
    assert(strcmp(s, "hello") == 0);
    assert(List_length(list) == 0);
    assert(list == NULL);
}

static void
test_reverse()
{
    assert(List_reverse(NULL) == NULL);

    List_T list = List_list("hello", "how", NULL);
    assert(strcmp(list->first, "hello") == 0);

    list = List_reverse(list);
    assert(strcmp(list->first, "how") == 0);

    List_free(&list);
}

static void
map2int(void **x, void *cl)
{
    (void)cl;
    size_t n = (size_t)*x;
    *x = (void*)(n * 100);
}

static void
test_map()
{
    List_T list = List_list((void*)1, (void*)2, (void*)3, NULL); 
    List_map(list, map2int, NULL);
    assert(list->first == (void*)100);
    List_free(&list);
}

static void
test_array()
{
    void **array;
    array = List_toArray(NULL, NULL);
    assert(array[0] == NULL);
    FREE(array);

    List_T list1 = List_list("hello", "how", NULL);
    array = List_toArray(list1, NULL);
    assert(strcmp(array[0], "hello") == 0);
    assert(strcmp(array[1], "how") == 0);
    assert(array[2] == NULL);
    FREE(array);
}

static void
free_data(void **x, void *cl)
{
    (void)cl;
    free(*x);
}

static void
test_freeall()
{
    // test free all the data and node
    // use List_map
    List_T list1 = List_list(malloc(4), malloc(4), NULL);
    List_map(list1, free_data, NULL);
    List_free(&list1);
    assert(list1 == NULL);

    // use List_pop
    List_T list2 = List_list(malloc(4), malloc(4), NULL);
    int n = List_length(list2);
    void *x;
    for (; n; n--) {
        list2 = List_pop(list2, &x);
        FREE(x);
    }
    assert(list2 == NULL);
}

int main()
{
    test_create();
    test_append();
    test_copy();
    test_push_pop();
    test_reverse();
    test_map();
    test_array();

    test_freeall();
    return 0;
}

